package com.me.bbs.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.me.bbs.dto.CommentDTO;
import com.me.bbs.dto.SubCommentDTO;
import com.me.bbs.entity.Comment;
import com.me.bbs.entity.CommentLike;
import com.me.bbs.entity.LatestReadAt;
import com.me.bbs.entity.Post;
import com.me.bbs.mapper.CommentMapper;
import com.me.bbs.service.ICommentLikeService;
import com.me.bbs.service.ICommentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.me.bbs.service.ILatestReadAtService;
import com.me.bbs.service.IPostService;
import com.me.bbs.vo.CommentVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lm
 * @since 2023-01-09
 */
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements ICommentService {

    @Resource
    IPostService postService;
    @Resource
    CommentMapper commentMapper;
    @Resource
    ICommentLikeService commentLikeService;
    @Resource
    ILatestReadAtService latestReadAtService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOne(CommentVO commentVO) throws SQLException {
        Comment comment = new Comment();
        BeanUtils.copyProperties(commentVO, comment);
        if (comment.getType() == 0) {
            Integer floor = commentMapper.selectMaxFloor(comment.getPost());
            comment.setFloor(floor + 1);
        }
        boolean commentSaveOk = this.save(comment);
        if (!commentSaveOk)
            throw new SQLException("保存评论失败");
        boolean postSaveOk = postService.lambdaUpdate()
                .eq(Post::getPostId, comment.getPost())
                .set(Post::getLastReplyAt, comment.getCreateAt())
                .setSql("comment_count = comment_count + 1")
                .update();
        if (!postSaveOk)
            throw new SQLException("保存评论失败");
        return comment.getCommentId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Page<CommentDTO> getByPostInPage(Page<CommentDTO> page, Long postId, Long cid) {
        Page<CommentDTO> commentDTOPage = commentMapper.selectByPostInPage(page, postId, cid);
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder
                .getRequestAttributes())).getRequest();
        HttpSession session = request.getSession();
        Object userIdObj = session.getAttribute("user");
        if (userIdObj == null || commentDTOPage.getTotal() == 0) return commentDTOPage;
        Long userId = (Long) session.getAttribute("user");
        List<CommentDTO> records = commentDTOPage.getRecords();
        List<Long> cids = records.stream().map(CommentDTO::getCommentId).collect(Collectors.toList());
        records.forEach(c -> {
            c.getSubComments().forEach(s -> {
                cids.add(s.getCommentId());
            });
        });
        List<CommentLike> commentLikeList = commentLikeService.lambdaQuery().in(
                        !cids.isEmpty()
                        , CommentLike::getCommentId, cids)
                .eq(CommentLike::getUser, userId)
                .select(CommentLike::getCommentId)
                .list();
        List<Long> likedCommentIdList = commentLikeList.stream()
                .map(CommentLike::getCommentId).collect(Collectors.toList());
        records.forEach(c -> {
            c.setIsLike(likedCommentIdList.contains(c.getCommentId()));
            c.getSubComments().forEach(s -> {
                s.setIsLike(likedCommentIdList.contains(s.getCommentId()));
            });
        });
        return commentDTOPage;
    }

    @Override
    public CommentDTO getCommentById(Long id) {
        return commentMapper.selectCommentDTOById(id);
    }

    @Override
    public SubCommentDTO getCommentById(Long id, boolean isSub) {
        return commentMapper.selectSubCommentDTOById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByCid(Long id) throws SQLException {
        Comment comment = this.getById(id);
        Byte type = comment.getType();
        //如果评论类型为子评论则更新帖子评论数量减一
        if (type == 1) {
            postService.lambdaUpdate()
                    .setSql("comment_count = comment_count - 1")
                    .eq(Post::getPostId, comment.getPost())
                    .update();
            this.removeById(id);
            return;
        }
        //否则减少帖子评论数量为子评论数量加自身
        Long subCommentCount = this.lambdaQuery().eq(Comment::getParentComment, comment.getCommentId())
                .count();
        boolean isPostOk = postService.lambdaUpdate()
                .setSql("comment_count = comment_count - " + (subCommentCount + 1))
                .eq(Post::getPostId, comment.getPost())
                .update();
        if (!isPostOk)
            throw new SQLException("更新帖子评论数出错");
        //删除所有子评论
        this.lambdaUpdate()
                .eq(Comment::getParentComment, comment.getCommentId())
                .remove();
        //删除自身
        this.removeById(id);
    }

    @Override
    public Page<Map<String, Object>> getLatestComments(Page<Comment> page, Map<String, Object> argsMap) {
        LatestReadAt latestReadAt = latestReadAtService.lambdaQuery()
                .eq(LatestReadAt::getUserId, argsMap.get("uid")).one();
        if (latestReadAt == null) {
            latestReadAt = new LatestReadAt();
            latestReadAt.setUserId((Long) argsMap.get("uid"));
        }
        latestReadAt.setReadReplyAt((LocalDateTime) argsMap.get("t"));
        latestReadAtService.saveOrUpdate(latestReadAt);
        return commentMapper.selectLatest(page, argsMap);
    }

    @Override
    public Long getUnreadCount(Map<String, Object> argsMap) {
        LatestReadAt latestReadAt = latestReadAtService.lambdaQuery()
                .eq(LatestReadAt::getUserId, argsMap.get("uid")).one();
        LocalDateTime latestReadTime = null;
        if(latestReadAt != null)
            latestReadTime = latestReadAt.getReadReplyAt();
        argsMap.put("latestTime", latestReadTime);
        return commentMapper.getUnreadCount(argsMap);
    }


}
